/*
 * @Author: liusheng
 * @Date: 2022-05-17 08:45:30
 * @LastEditors: liusheng
 * @LastEditTime: 2022-05-17 12:17:45
 * @Description: 剑指 Offer II 063. 替换单词
 * email:liusheng613@126.com
 * Copyright (c) 2022 by liusheng/liusheng, All Rights Reserved. 
 * 
 剑指 Offer II 063. 替换单词
在英语中，有一个叫做 词根(root) 的概念，它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词(successor)。例如，词根an，跟随着单词 other(其他)，可以形成新的单词 another(另一个)。

现在，给定一个由许多词根组成的词典和一个句子，需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根，则用最短的词根替换它。

需要输出替换之后的句子。

 

示例 1：

输入：dictionary = ["cat","bat","rat"], sentence = "the cattle was rattled by the battery"
输出："the cat was rat by the bat"
示例 2：

输入：dictionary = ["a","b","c"], sentence = "aadsfasf absbs bbab cadsfafs"
输出："a a b c"
示例 3：

输入：dictionary = ["a", "aa", "aaa", "aaaa"], sentence = "a aa a aaaa aaa aaa aaa aaaaaa bbb baba ababa"
输出："a a a a a a a a bbb baba a"
示例 4：

输入：dictionary = ["catt","cat","bat","rat"], sentence = "the cattle was rattled by the battery"
输出："the cat was rat by the bat"
示例 5：

输入：dictionary = ["ac","ab"], sentence = "it is abnormal that this solution is accepted"
输出："it is ab that this solution is ac"
 

提示：

1 <= dictionary.length <= 1000
1 <= dictionary[i].length <= 100
dictionary[i] 仅由小写字母组成。
1 <= sentence.length <= 10^6
sentence 仅由小写字母和空格组成。
sentence 中单词的总量在范围 [1, 1000] 内。
sentence 中每个单词的长度在范围 [1, 1000] 内。
sentence 中单词之间由一个空格隔开。
sentence 没有前导或尾随空格。
 

注意：本题与主站 648 题相同： https://leetcode-cn.com/problems/replace-words/

通过次数8,638 提交次数12,220
 */

#include "header.h"

/*
prefix tree(Trie) solution
Trie is use unordered_map,can also use array of Trie *,which
can speed up a little but use a little more space consumption
*/
class Solution {
    class Trie
    {
    public:
        Trie()
        {
            isWord = false;
        }

        void insert(const string & word)
        {
            Trie * node = this;
            for (char ch : word)
            {
                if (!node->children.count(ch))
                {
                    node->children[ch] = new Trie();
                }
                node = node->children[ch];
            }
            node->isWord = true;
        }

        bool contains(const string & word)
        {
            Trie * node = this;
            for (int i = 0; i < word.size(); ++i)
            {
                char ch = word[i];
                if (!node->children.count(ch))
                {
                    return false;
                }
                node = node->children[ch];
            }
            return node->isWord;
        }
    private:
        unordered_map<char,Trie *> children;
        bool isWord;
    };

public:
    Solution()
    {
        root = new Trie();
    }
    string replaceWords(vector<string>& dictionary, string sentence) {
        buildTrie(dictionary);
        size_t n = sentence.size();

        string word;
        string result;
        int i = 0;
        for (i = 0; i < n; ++i)
        {
            if (sentence[i] == ' ')
            {
                result += word;
                result += ' ';
                word.clear();
            }
            else
            {
                word += sentence[i];
                if (root->contains(word))
                {
                    size_t pos = string::npos;
                    if (( pos = sentence.find(' ',i)) != string::npos)
                    {
                        i = pos - 1;
                    }
                    else
                    {
                        break;
                    }               
                }
            }
        }

        result += word;
        return result;
    }
private:
    void buildTrie(const vector<string>& dictionary)
    {
        for (auto word : dictionary)
        {
            root->insert(word);
        }
    }
private:
    Trie * root;
};

/*
using hash map solution
this will use less space,but a little bit more time
*/
class Solution {
public:
    string replaceWords(vector<string>& dictionary, string sentence) {
        unordered_set<string> dic;
        for (auto str : dictionary)
        {
            dic.insert(str);
        }
        
        int n = sentence.size();
        string result;
        string word;
        for (int i = 0; i < n; ++i)
        {
            if (sentence[i] == ' ')
            {
                result += word;
                result += ' ';
                word.clear();
            }
            else
            {
                word += sentence[i];
                if (dic.count(word))
                {
                    int nextBlankPos = sentence.find(' ',i);
                    if (nextBlankPos == string::npos)
                    {
                        break;
                    }
    
                    //after ++i in for loop,will go to next blank
                    i = nextBlankPos - 1;
                }
            }
        }
        
        result += word;
        return result;
    }
};